package mmpathgen.core;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import mmpathgen.callgraphanalysis.CallGraphAnalysis;
import mmpathgen.callgraphanalysis.CallGraphGenByCHA;
import mmpathgen.callgraphanalysis.CallGraphGenBySpark;
import mmpathgen.callgraphanalysis.CallGraphVisitor;
import mmpathgen.callgraphanalysis.FilteredCallGraph;
import mmpathgen.callgraphanalysis.UnfilteredCallGraph;
import mmpathgen.controlflowanalysis.CFGGenByBriefUnitGraph;
import mmpathgen.controlflowanalysis.ControlFlowAnalysis;
import mmpathgen.util.JRELibFilter;
import soot.SootMethod;
import soot.Unit;
import soot.jimple.InvokeStmt;
import soot.jimple.Stmt;
import soot.jimple.toolkits.annotation.purity.SootMethodFilter;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.options.Options;
import soot.toolkits.graph.DirectedGraph;
import soot.toolkits.graph.UnitGraph;

public class CopyOfMMPathGen_20100603_BeforeRealPathGen {

	// private static int level = 0;
	private static int sumcfgsize = 0;

	static {
		soot.options.Options.v().set_keep_line_number(true);
	}

	/**
	 *@param args
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub

		// get the class names automatically
		File path = new File("/export/home/weihe/MMPath/jtopas_v3");

		// set main application class
		String mainClassName = "jtopas.ContentsExtractor";
		
		// package name
		String srcFolder = "jtopas_v3";

		mmpathGen_CHA_Filted_Brief(path, srcFolder, mainClassName);
		mmpathGen_CHA_Unfilted_Brief(path, srcFolder, mainClassName);

	}

	/**
	 * @param path
	 * @param mainClassName
	 * @throws FileNotFoundException
	 */
	private static void mmpathGen_CHA_Filted_Brief(File path,
			String srcFolder, String mainClassName) throws FileNotFoundException {

		String cgaType = "CHA";
		String filtType = "Filted";
		String cfaType = "Brief";
		
		Options.v().set_output_dir("sootOutput." + srcFolder + "." + mainClassName + "." + cgaType + "_" + filtType + "_" + cfaType);

		// redirecting the standard output
		PrintStream console = System.out;
		PrintStream redirectedOut = new PrintStream(new BufferedOutputStream(
				new FileOutputStream(mainClassName + "_CHA_Filted_Brief.txt")),
				true);
		System.setOut(redirectedOut);

		// construct call graph

		long startTime = System.currentTimeMillis();

		CallGraph cg = CallGraphGenByCHA.cgGen(path, mainClassName);

		SootMethodFilter filter = new JRELibFilter();

		FilteredCallGraph fcg = CallGraphAnalysis.getFilteredCallGraph(cg, filter);

		CallGraphAnalysis.printCallGraphAsDot(fcg, mainClassName, cgaType,
				filtType, cfaType);

		Map<SootMethod, Set<List<Unit>>> atomicPathMap = genPotentialSubPathMap(
				fcg, mainClassName, cgaType, filtType, cfaType);

		System.out.println("total unit nodes of cfgs: " + sumcfgsize);

		// System.out.println("atomicPathMap: " + atomicPathMap);

		// genMMPath(fcg, atomicPathMap);

		System.out.println("total potential MM-Path: " + fcg.getTails().size());

		long finishTime = System.currentTimeMillis();

		long costTime = finishTime - startTime;

		long costTimeInSec = costTime / 1000;

		System.out.println("costTimeInSec: " + costTimeInSec);

		// close the standard output redirection
		redirectedOut.close();
		System.setOut(console);
	}

	/**
	 * @param path
	 * @param mainClassName
	 * @throws FileNotFoundException
	 */
	private static void mmpathGen_Spark_Filted_Brief(File path, String srcFolder,
			String mainClassName) throws FileNotFoundException {

		String cgaType = "Spark";
		String filtType = "Filted";
		String cfaType = "Brief";
		
		Options.v().set_output_dir("sootOutput." + srcFolder + "." + mainClassName + "." + cgaType + "_" + filtType + "_" + cfaType);

		// redirecting the standard output
		PrintStream console = System.out;
		PrintStream redirectedOut = new PrintStream(
				new BufferedOutputStream(new FileOutputStream(mainClassName
						+ "_Spark_Filted_Brief.txt")), true);
		System.setOut(redirectedOut);

		// construct call graph

		long startTime = System.currentTimeMillis();

		CallGraph cg = CallGraphGenBySpark.cgGen(path, mainClassName);

		SootMethodFilter filter = new JRELibFilter();

		FilteredCallGraph fcg = CallGraphAnalysis.getFilteredCallGraph(cg, filter);

		CallGraphAnalysis.printCallGraphAsDot(fcg, mainClassName, cgaType,
				filtType, cfaType);

		Map<SootMethod, Set<List<Unit>>> atomicPathMap = genPotentialSubPathMap(
				fcg, mainClassName, cgaType, filtType, cfaType);

		System.out.println("total unit nodes of cfgs: " + sumcfgsize);

		// System.out.println("atomicPathMap: " + atomicPathMap);

		// genMMPath(fcg, atomicPathMap);

		System.out.println("total potential MM-Path: " + fcg.getTails().size());

		long finishTime = System.currentTimeMillis();

		long costTime = finishTime - startTime;

		long costTimeInSec = costTime / 1000;

		System.out.println("costTimeInSec: " + costTimeInSec);

		// close the standard output redirection
		redirectedOut.close();
		System.setOut(console);
	}

	/**
	 * @param path
	 * @param mainClassName
	 * @throws FileNotFoundException
	 */
	private static void mmpathGen_CHA_Unfilted_Brief(File path, String srcFolder, 
			String mainClassName) throws FileNotFoundException {

		String cgaType = "CHA";
		String filtType = "Unfilted";
		String cfaType = "Brief";

		Options.v().set_output_dir("sootOutput." + srcFolder + "." + mainClassName + "." + cgaType + "_" + filtType + "_" + cfaType);
		
		// redirecting the standard output
		PrintStream console = System.out;
		PrintStream out = new PrintStream(
				new BufferedOutputStream(new FileOutputStream(mainClassName
						+ "_CHA_Unfilted_Brief.txt")), true);
		System.setOut(out);

		// construct call graph

		long startTime = System.currentTimeMillis();

		CallGraph cg = CallGraphGenByCHA.cgGen(path, mainClassName);

		UnfilteredCallGraph ucg = CallGraphAnalysis.getUnfilteredCallGraph(cg);

		CallGraphAnalysis.printCallGraphAsDot(ucg, mainClassName, cgaType,
				filtType, cfaType);

		Map<SootMethod, Set<List<Unit>>> atomicPathMap = genPotentialSubPathMap(
				ucg, mainClassName, cgaType, filtType, cfaType);

		System.out.println("total unit nodes of cfgs: " + sumcfgsize);

		// System.out.println("atomicPathMap: " + atomicPathMap);

		// genMMPath(ucg, atomicPathMap);

		System.out.println("total potential MM-Path: "
				+ CallGraphAnalysis.getTails(cg).size());

		long finishTime = System.currentTimeMillis();

		long costTime = finishTime - startTime;

		long costTimeInSec = costTime / 1000;

		System.out.println("costTimeInSec: " + costTimeInSec);

		// close the standard output redirection
		out.close();
		System.setOut(console);
	}

	/**
	 * @param path
	 * @param mainClassName
	 * @throws FileNotFoundException
	 */
	private static void mmpathGen_Spark_Unfilted_Brief(File path, String srcFolder, 
			String mainClassName) throws FileNotFoundException {

		String cgaType = "Spark";
		String filtType = "Unfilted";
		String cfaType = "Brief";

		Options.v().set_output_dir("sootOutput." + srcFolder + "." + mainClassName + "." + cgaType + "_" + filtType + "_" + cfaType);
		
		// redirecting the standard output
		PrintStream console = System.out;
		PrintStream out = new PrintStream(
				new BufferedOutputStream(new FileOutputStream(mainClassName
						+ "_Spark_Unfilted_Brief.txt")), true);
		System.setOut(out);

		// construct call graph

		long startTime = System.currentTimeMillis();

		CallGraph cg = CallGraphGenBySpark.cgGen(path, mainClassName);

		UnfilteredCallGraph ucg = CallGraphAnalysis.getUnfilteredCallGraph(cg);

		CallGraphAnalysis.printCallGraphAsDot(ucg, mainClassName, cgaType,
				filtType, cfaType);

		Map<SootMethod, Set<List<Unit>>> atomicPathMap = genPotentialSubPathMap(
				ucg, mainClassName, cgaType, filtType, cfaType);

		System.out.println("total unit nodes of cfgs: " + sumcfgsize);

		// System.out.println("atomicPathMap: " + atomicPathMap);

		// genMMPath(ucg, atomicPathMap);

		System.out.println("total potential MM-Path: "
				+ CallGraphAnalysis.getTails(cg).size());

		long finishTime = System.currentTimeMillis();

		long costTime = finishTime - startTime;

		long costTimeInSec = costTime / 1000;

		System.out.println("costTimeInSec: " + costTimeInSec);

		// close the standard output redirection
		out.close();
		System.setOut(console);
	}

	public static Map<SootMethod, Set<List<Unit>>> genPotentialSubPathMap(
			DirectedGraph<SootMethod> cg, String mainClass, String cgaType,
			String filtType, String cfaType) {

		List<SootMethod> mtdList = new ArrayList<SootMethod>();
		if (cg instanceof UnfilteredCallGraph) {
			mtdList = CallGraphVisitor
					.visitNodesInTopologicalOrder((UnfilteredCallGraph) cg);
		} else if (cg instanceof FilteredCallGraph) {
			mtdList = CallGraphVisitor
					.visitNodesInTopologicalOrder((FilteredCallGraph) cg);
		}
		Map<SootMethod, Set<List<Unit>>> potentialSubPathMap = new HashMap<SootMethod, Set<List<Unit>>>();

		List<SootMethod> visitedMtdList = new ArrayList<SootMethod>();
		for (SootMethod mtd : mtdList) {

			if (!visitedMtdList.contains(mtd)) {
				List<SootMethod> tgts = cg.getSuccsOf(mtd);

				Set<List<Unit>> atomicPathSet = new HashSet<List<Unit>>();

				String clsName = mtd.getDeclaringClass().getName();
				String mtdName = mtd.getName();

				UnitGraph cfg = CFGGenByBriefUnitGraph.cfgGen(clsName, mtdName);

				if (cfg != null) {

					ControlFlowAnalysis.printCFGAsDot(cfg, clsName, mtd
							.getName(), mainClass, cgaType, filtType, cfaType);

					System.out.println("unit nodes of " + clsName + "."
							+ mtdName + ": " + cfg.size());
					sumcfgsize += cfg.size();

					// get all possible independent path
					List<Unit> heads = cfg.getHeads();
					List<Unit> tails = cfg.getTails();

					for (Unit u : cfg) {
						if (u instanceof InvokeStmt) {
							SootMethod currentMtd = ((InvokeStmt) u)
									.getInvokeExpr().getMethod();
							if (tgts.contains(currentMtd)) {
								for (Unit head : heads) {

									List<Unit> neckFragment = cfg
											.getExtendedBasicBlockPathBetween(
													head, u);

									for (Unit tail : tails) {

										List<Unit> legFragment = cfg
												.getExtendedBasicBlockPathBetween(
														u, tail);

										List<Unit> atomicPath = new ArrayList<Unit>();
										if (neckFragment != null
												&& legFragment != null) {
											atomicPath.addAll(neckFragment);
											atomicPath.addAll(legFragment);
											if (!atomicPathSet
													.contains(atomicPath))
												atomicPathSet.add(atomicPath);
										}

									}

								}

							}
						}

					}
					potentialSubPathMap.put(mtd, atomicPathSet);

				}
			}
		}
		return potentialSubPathMap;
	}

	public static void genMMPath(FilteredCallGraph fcg,
			Map<SootMethod, Set<List<Unit>>> atomicPathMap) {

		System.out.println("genMMPath");
		List<SootMethod> heads = fcg.getHeads();
		for (SootMethod head : heads) {
			genSubMMPath(head, atomicPathMap, fcg);

		}
	}

	private static void genSubMMPath(SootMethod mtd,
			Map<SootMethod, Set<List<Unit>>> atomicPathMap, FilteredCallGraph fcg) {

		System.out.println("genSubMMPath");
		Set<List<Unit>> atomicPathSet = atomicPathMap.get(mtd);
		System.out.println("curMtd: " + mtd.getName());
		System.out.println("atomicPathSet: " + atomicPathSet);

		for (List<Unit> atomicPath : atomicPathSet) {
			for (Unit unit : atomicPath) {
				List<SootMethod> tgts = fcg.getSuccsOf(mtd);
				SootMethod curMtd = ((InvokeStmt) unit).getInvokeExpr()
						.getMethod();

				System.out.println(mtd.getDeclaringClass() + "."
						+ mtd.getName() + "<");
				if (unit instanceof InvokeStmt && tgts.contains(curMtd)) {
					// self-defined method
					genSubMMPath(curMtd, atomicPathMap, fcg);

				} else if (unit instanceof Stmt) {
					int lineNo = ControlFlowAnalysis.getLineNumber((Stmt) unit);
					System.out.println(lineNo + ", ");

				}

			}
		}
	}
}
